package top.sqdpt.third.utils;

import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.client.JdkClientHttpRequestFactory;
import org.springframework.web.client.RestClient;
import org.springframework.web.client.RestClientException;

import java.net.http.HttpClient;
import java.time.Duration;
import java.util.Map;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * @author pengtao
 * @date 2025/4/7
 * @Descciption
 **/
public class HttpUtil {
    // 单例实例
    private static final HttpUtil INSTANCE = new HttpUtil();
    private final RestClient restClient;

    // 私有构造器
    private HttpUtil() {
        this.restClient = RestClient.builder()
                .requestFactory(createRequestFactory())
                .defaultStatusHandler(shouldRetry(), (req, res) -> {
                    throw new RestClientException("HTTP请求失败，状态码: " + res.getStatusCode());
                })
                .build();
    }

    // 获取单例
    public static HttpUtil getInstance() {
        return INSTANCE;
    }

    // 配置超时和重试的 RequestFactory
    private JdkClientHttpRequestFactory createRequestFactory() {
        HttpClient httpClient = HttpClient.newBuilder()
                .connectTimeout(Duration.ofMinutes(3))
                .followRedirects(HttpClient.Redirect.NORMAL)
                .build();
        return new JdkClientHttpRequestFactory(httpClient);
    }

    // 定义哪些情况需要重试（状态码5xx或IO异常）
    private Predicate<HttpStatusCode> shouldRetry() {
        return HttpStatusCode::is5xxServerError;
    }

    /**
     * GET 请求
     * @param url 请求地址
     * @return 响应体字符串
     */
    public String get(String url) {
        return executeWithRetry(() -> restClient.get()
                .uri(url)
                .retrieve()
                .body(String.class)
        );
    }
    
    /**
     * GET 请求（带参数）
     * @param url 请求地址
     * @param params 请求参数
     * @return 响应体字符串
     */
    public String get(String url, Map<String, Object> params) {
        return executeWithRetry(() -> restClient.get()
                .uri(buildUri(url, params))
                .retrieve()
                .body(String.class)
        );
    }

    /**
     * POST 请求（JSON 格式）
     * @param url 请求地址
     * @param body 请求体对象（自动序列化为JSON）
     * @return 响应体字符串
     */
    public String post(String url, Object body) {
        return executeWithRetry(() -> restClient.post()
                .uri(url)
                .contentType(MediaType.APPLICATION_JSON)
                .body(body)
                .retrieve()
                .body(String.class)
        );
    }
    
    /**
     * 构建带参数的URI
     * @param url 基础URL
     * @param params 参数
     * @return 完整URI
     */
    private String buildUri(String url, Map<String, Object> params) {
        if (params == null || params.isEmpty()) {
            return url;
        }
        
        StringBuilder uriBuilder = new StringBuilder(url);
        if (url.contains("?")) {
            uriBuilder.append("&");
        } else {
            uriBuilder.append("?");
        }
        
        boolean first = true;
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            if (!first) {
                uriBuilder.append("&");
            }
            uriBuilder.append(entry.getKey()).append("=").append(entry.getValue());
            first = false;
        }
        
        return uriBuilder.toString();
    }

    // 带重试的执行逻辑（最多3次）
    private <T> T executeWithRetry(Supplier<T> action) {
        int maxRetries = 3;
        int retryCount = 0;
        Exception lastException = null;

        while (retryCount < maxRetries) {
            try {
                return action.get();
            } catch (RestClientException e) {
                lastException = e;
                retryCount++;
                if (retryCount < maxRetries) {
                    System.out.printf("请求失败，第 %d 次重试...%n", retryCount);
                    try { Thread.sleep(1000 * retryCount); } catch (InterruptedException ignored) {}
                }
            }
        }
        throw new RuntimeException("HTTP请求失败，重试次数耗尽", lastException);
    }
}
